package com.cqsd.ioc.core.context;

import com.cqsd.ioc.trait.BeanInfo;
import com.cqsd.string.StringUtil;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author caseycheng
 * @date 2023/4/9-00:22
 **/
public class BeanManager {
    private final Map<String, BeanInfo> beanNameInfoMap = new ConcurrentHashMap<>();
    private final Map<Class<?>, BeanInfo> beanClassInfoMap = new ConcurrentHashMap<>();
    private final Set<BeanInfo> beanInfoSet = new HashSet<>();
    private final SingleObjects singleObjects = new SingleObjects();


    private BeanManager() {
    }

    public static BeanManager getInstance() {
        return new BeanManager();
    }

    public SingleObjects getSingleObjects() {
        return this.singleObjects;
    }

    public BeanInfo getBeanInfo(String beanName) {
        Objects.requireNonNull(beanName);
        return beanNameInfoMap.get(beanName);
    }

    public BeanInfo getBeanInfo(Class<?> type) {
        Objects.requireNonNull(type);
        return beanClassInfoMap.get(type);
    }

    public void add(BeanInfo beanInfo) {
        final var name = beanInfo.getName();
        final Class<?> type = beanInfo.getType();
        this.beanNameInfoMap.put(name, beanInfo);
        this.beanClassInfoMap.put(type, beanInfo);
        this.beanInfoSet.add(beanInfo);
    }

    public void add(Collection<BeanInfo> beanInfos) {
        Objects.requireNonNull(beanInfos);
        if (beanInfos.isEmpty()) {
            return;
        }
        for (BeanInfo beanInfo : beanInfos) {
            this.add(beanInfo);
        }
    }

    public void add(BeanInfo[] beanInfos) {
        Objects.requireNonNull(beanInfos);
        this.add(List.of(beanInfos));
    }

    public Collection<BeanInfo> getBeanInfos() {
        return this.beanInfoSet;
    }

    public Map<String, BeanInfo> beanNameInfoMap() {
        return beanNameInfoMap;
    }

    public Map<Class<?>, BeanInfo> beanClassInfoMap() {
        return beanClassInfoMap;
    }

    public int getSize() {
        return this.beanInfoSet.size();
    }

    public boolean contains(Class<?> clazz) {
        return this.beanClassInfoMap.containsKey(clazz);
    }

    public static class SingleObjects {
        public boolean containsBeanName(String beanName) {
            return this.singleValue.containsKey(beanName);
        }

        static class SingleObjectException extends RuntimeException {
            /**
             * Constructs a new runtime exception with the specified detail message.
             * The cause is not initialized, and may subsequently be initialized by a
             * call to {@link #initCause}.
             *
             * @param message the detail message. The detail message is saved for
             *                later retrieval by the {@link #getMessage()} method.
             */
            public SingleObjectException(String message) {
                super(message);
            }
        }

        private final Map<String, Object> singleValue = new HashMap<>();

        public void set(String beanName, Object bean){
            if (!singleValue.containsKey(beanName)) {
                this.singleValue.put(beanName, bean);
            } else {
                throw new SingleObjectException("bean对象已存在");
            }
        }

        public Object get(String beanName) {
            if (StringUtil.hasLength(beanName)) {
                return this.singleValue.get(beanName);
            } else {
                return null;
            }
        }
        public int size(){
            return this.singleValue.size();
        }
    }
}
